#include <EEPROM.h>
#include <stdio.h>
#include <string.h>
#include <DS1302.h>
#include <LiquidCrystal.h>
#include <CapSense.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Tone.h>
#include <stdio.h>
#include <avr/pgmspace.h>
#include <phi_big_font.h>
#include <Time.h>
#include <HomeEasy.h>
HomeEasy homeEasy;


Tone tone1;

byte com = 0; //reply from voice recognition
#define LCDLED 2
#define OCTAVE_OFFSET 0
String Screens[10];
boolean NormalInfo = true;
String RFID = "1";

boolean color = false;

int notes[] = { 
  0,
  NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4,
  NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5,
  NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6,
  NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7
};
char *song = "The Simpsons:d=4,o=5,b=160:c.6,e6,f#6,8a6,g.6,e6,c6,8a,8f#,8f#,8f#,2g,8p,8p,8f#,8f#,8f#,8g,a#.,8c6,8c6,8c6,c6";

boolean fadescreen = true;
boolean screenLEDstate = true;

/* Set the appropriate digital I/O pin connections */
uint8_t CE_PIN   = 6;
uint8_t IO_PIN   = 4;
uint8_t SCLK_PIN = 3;

boolean runalarm = false;
boolean AlarmActive = false;
boolean AlarmSent = false;

LiquidCrystal lcd(29, 31, 33, 35, 37, 39);
boolean inverted=false;
boolean ActiveSnooze = false;
CapSense  snooze = CapSense(48,46);        // 10M resistor between pins 4 & 2, pin 2 is sensor pin, add a wire and or foil if desired

long CapSenseMax=0;
long CapSenseMin=50000;
long CapSenselast=0;

#define ONE_WIRE_BUS 49
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress TempAdd;
float tempC;

struct RemoteMSG {
  char *ReceivedDataRAW;
  String ReceivedData;
  char *Lastmsg;  
  unsigned int DispTime;
  unsigned long DispType;
  boolean Active;
  boolean Actioned;
  boolean Sound;
} 
remotemsg = {
  "", "", "", 30, 0, false, false, false};

struct VOCcmd {
  String command;
  boolean active;
  long trig;
} 
voice[15] = {
  "", false, 0};


struct Timing {
  unsigned long current;
  unsigned long previous;
  unsigned long temps;
  unsigned long snooze;
  unsigned long screenchange;
  unsigned long flashscreen;
  unsigned long flashled;
  int delta;
} 
time = { 
  0, 0, 0, 0, 0, 0, 0, 0};

struct Alarms {
  unsigned long DOW;
  unsigned long Hour;
  unsigned long Minute;
} 
alarm[5] = { 
  0, 0, 0};

struct CapSensor {
  boolean CurrentState;
  boolean PreviousState;
  int CapCount;
  int delta;
} 
capsensor = { 
  false, false, 0, 0};

/* Create buffers */
char buf[50];
char timebuf[50];
char datebuf[50];
char day1[10];
char Secs[50];

/* Create a DS1302 object */
DS1302 rtc(CE_PIN, IO_PIN, SCLK_PIN);


void print_time()
{
  /* Get the current time and date from the chip */
  Time t = rtc.time();

  /* Name the day of the week */
  memset(day1, 0, sizeof(day1));  /* clear day buffer */
  switch (t.day) {
  case 1:
    strcpy(day1, "Sunday");
    break;
  case 2:
    strcpy(day1, "Monday");
    break;
  case 3:
    strcpy(day1, "Tuesday");
    break;
  case 4:
    strcpy(day1, "Wednesday");
    break;
  case 5:
    strcpy(day1, "Thursday");
    break;
  case 6:
    strcpy(day1, "Friday");
    break;
  case 7:
    strcpy(day1, "Saturday");
    break;
  }

  /* Format the time and date and insert into the temporary buffer */
  snprintf(buf, sizeof(buf), "%s %04d-%02d-%02d %02d:%02d:%02d",
  day1,
  t.yr, t.mon, t.date,
  t.hr, t.min, t.sec);

  /* Print the formatted string to serial so we can see the time */
  //  Serial.println(buf);
}

byte img1[]={
  0,3,3,4,4,4,3,3,0,0,0,3,3,4,4,4,3,3,0,0,
  6,1,4,4,7,4,4,1,6,4,6,1,4,4,4,4,4,1,6,0,
  3,4,1,1,7,1,1,4,3,1,3,4,1,1,1,1,1,4,3,0,
  0,6,6,1,1,1,6,6,0,0,0,6,6,1,1,1,6,6,0,0
};// line 0, 2, 1, 3 order.


String txtMsg = "";                         // a string for incoming text
int lastStringLength = txtMsg.length();     // previous length of the String

void setup()
{


  if (EEPROM.read(50) == 1){
    AlarmActive = true;
  }
  else{
    AlarmActive = false;
  }  

  Serial.begin(115200);
  Serial1.begin(9600);
  Serial2.begin(9600);
  pinMode(A15,INPUT);
  pinMode(LCDLED,OUTPUT);
  homeEasy = HomeEasy();
  homeEasy.init();

  sendRF(0,1,1,0);
  delay(2000);
  sendRF(0,1,0,0);
  snooze.set_CS_AutocaL_Millis(0xFFFFFFFF);     // turn off autocalibrate on channel 1 - just as an example
  sensors.requestTemperatures(); // Send the command to get temperatures
  sensors.getAddress(TempAdd, 0);
  sensors.setResolution(TempAdd, 9);
  Serial.println(sensors.getResolution(TempAdd), DEC); 
  tempC = sensors.getTempC(TempAdd);
  tone1.begin(47);
  //  play_rtttl(song);
  /* Initialize a new chip by turning off write protection and clearing the
   clock halt flag. These methods needn't always be called. See the DS1302
   datasheet for details. */
  rtc.write_protect(false);
  rtc.halt(false);

  // set up the LCD's number of columns and rows: 
  lcd.begin(20, 4);
  init_big_font(&lcd);
  analogWrite(LCDLED,255);
  show_arduino();
  loadtimers();

  Serial1.write(0xAA);
  Serial1.write(0x37);
  delay(1000);
  Serial1.write(0xAA);
  Serial1.write(0x21);
  delay(1000);
  Serial1.write(0xAA);
  Serial1.write(0x22);
  delay(1000);
  Serial1.write(0xAA);
  Serial1.write(0x23);

  /* Make a new time object to set the date and time */
  /*   Tuesday, May 19, 2009 at 21:16:37.            */
  //  Time t(2012, 5, 7, 16, 14, 00, 2);
  //
  //  /* Set the time and date on the chip */
  //   rtc.time(t);
  lcd.setCursor(0, 0);
  lcd.print("Loading");
}
boolean receivingdata = false;
void alertmsg(String MSG){
  clearalertmsg();
  lcd.setCursor(7, 4);
  lcd.print(MSG);
}
void clearalertmsg(){

  lcd.setCursor(7, 4);
  lcd.print("       ");
}
/* Loop and print the time every second */
void loop()
{
  time.current = millis();
  time.delta = (int) (time.current - time.previous);
  time.previous = time.current;

  if(time.current > (time.flashscreen + 1000)) { // 5 mins
    //    Serial2.println(millis());
    if( color){
      digitalWrite(41,HIGH); 
      digitalWrite(43,LOW); 
    }
    else{
      digitalWrite(41,LOW); 
      digitalWrite(43,HIGH);      
    }
    color = !color;
    time.flashscreen = time.current;
  }


  //  Serial.println(millis());
  if(time.current > (time.snooze + 300000)) { // 5 mins
    ActiveSnooze = false;
  }


  if(time.current > (time.temps + 60000)) { // 5 mins
    sensors.requestTemperatures(); // Send the command to get temperatures
    tempC = sensors.getTempC(TempAdd);

    lcd.setCursor(0, 4);
    lcd.print(tempC);    
    lcd.print("c");

    time.temps = time.current;   
  }

  long start = millis();
  if(snooze.capSense(30) > CapSenseMax){
    CapSenseMax = snooze.capSense(30);
  }
  if(snooze.capSense(30) < CapSenseMin){
    CapSenseMin = snooze.capSense(30);
  }

  long smoothcapsense = smooth(snooze.capSense(30), CapSenselast,3); 

  CapSenselast = snooze.capSense(30);
  //  Serial.println(smoothcapsense);
  //  Serial.println(CapSenselast);
  //    acceld.value[2] = smooth((int)compass.a.z, acceld.prev[2], acceld.factor);    

  if(smoothcapsense >100){
    capsensor.PreviousState = capsensor.CurrentState;
    capsensor.CurrentState = true;
    capsensor.CapCount ++;
    //    Serial.println(capsensor.CapCount);
  }
  else{
    capsensor.PreviousState = capsensor.CurrentState;
    capsensor.CurrentState = false; 
    capsensor.CapCount = 0;
  }    



  if(runalarm){
    if(capsensor.CurrentState && ActiveSnooze == false){
      fadescreen = false;
      ActiveSnooze = true;
      lcd.setCursor(7, 4);
      lcd.print("SNOOZE");    
      time.snooze = time.current;
      //      runalarm = false;  
    }
    if(!ActiveSnooze){
      fadescreen = true;
      lcd.setCursor(7, 4);
      lcd.print("WakeUp");    
      tone1.stop();
      tone1.play(NOTE_A6);
      delay(300);
      tone1.stop();
      delay(100);
      tone1.play(NOTE_A4);
      delay(300);
      tone1.stop();
    }
    if(ActiveSnooze){
      if( capsensor.CurrentState && capsensor.CurrentState == capsensor.PreviousState && capsensor.CapCount == 100){
        runalarm = false;
        fadescreen = false;
        ActiveSnooze = false;
        lcd.setCursor(7, 4);
        lcd.print("      ");
      }
    }
  }
  else{

    if( capsensor.CurrentState && capsensor.CurrentState == capsensor.PreviousState && capsensor.CapCount == 100){
      AlarmActive = !AlarmActive;
      if (AlarmActive){
        EEPROM.write(50, 1);        
      }
      else{
        EEPROM.write(50, 0);        
      }

    }

  }
  lcd.setCursor(14, 4);
  lcd.print("Alarm");
  lcd.print(AlarmActive);

  //  lcd.setCursor(0, 0);
  //  lcd.print(total1);

  print_time();
  // print the number of seconds since reset:
  Time t = rtc.time();

  /* Name the day of the week */
  memset(day1, 0, sizeof(day1));  /* clear day buffer */
  switch (t.day) {
  case 1:
    strcpy(day1, "Sunday");
    break;
  case 2:
    strcpy(day1, "Monday");
    break;
  case 3:
    strcpy(day1, "Tuesday");
    break;
  case 4:
    strcpy(day1, "Wednesday");
    break;
  case 5:
    strcpy(day1, "Thursday");
    break;
  case 6:
    strcpy(day1, "Friday");
    break;
  case 7:
    strcpy(day1, "Saturday");
    break;
  }
  snprintf(buf, sizeof(buf), "%02d:%02d:%02d %s %02d/%02d/%04d ", t.hr, t.min, t.sec, day1, t.date, t.mon, t.yr);
  snprintf(timebuf, sizeof(timebuf), "%02d %02d", t.hr, t.min);
  snprintf(datebuf, sizeof(datebuf), "%s %02d/%02d/%04d", day1, t.date, t.mon, t.yr);  
  snprintf(Secs, sizeof(Secs), "%02d", t.sec);  

  //  lcd_clear();
  //  lcd.setCursor(0, 0);
  //  lcd.print(timebuf);
  render_big_msg(timebuf,0,0);
  lcd.setCursor(9, 0);
  lcd.print(Secs);
  lcd.setCursor(0, 2);

  if(remotemsg.Active){
    fadescreen = false;
    if(time.current > (time.screenchange + 1000)) { // 5 mins
      if(!remotemsg.Actioned){
        if(remotemsg.Sound){
          play_rtttl(song);
          remotemsg.Actioned = true;
        }
      }
      if(remotemsg.DispTime > 0){
        lcd.setCursor(0, 2);

        String DispData;
        String Blank = " ";
        int BlankSpaceCount=0;
        DispData = remotemsg.ReceivedData;
        for( int b=0;b< (20 - remotemsg.ReceivedData.length()); b++){
          DispData += Blank;
        }

        lcd.print(DispData);
        remotemsg.DispTime --;
      }
      else{
        remotemsg.Active = false; 
      }
      time.screenchange = time.current;  
    }
  }
  else{
    lcd.print(datebuf); 
    fadescreen = true;
  }

  if(fadescreen){
    analogWrite(LCDLED,map(constrain(analogRead(A15),20,780),20,780,1,255)); 
  } 
  else{
    if(time.current > (time.flashscreen + 1000)) { // 5 mins
      if(screenLEDstate){
        analogWrite(LCDLED,255);
        screenLEDstate = !screenLEDstate;    
      }
      else{
        analogWrite(LCDLED,0);
        screenLEDstate = !screenLEDstate;
      }
      time.flashscreen = time.current;
    }
  }

  //  delay(1000);
  for(int i =0; i< 5; i++){
    if(alarm[i].DOW == t.day && alarm[i].Hour == t.hr && alarm[i].Minute == t.min){
      if(AlarmActive){
        if(!AlarmSent){
          //        Serial.println("AlarmActive");
          runalarm = true;
          AlarmSent = true;
        }
      }
    }
    else{
      AlarmSent = false;
    }
  }
}

#define isdigit(n) (n >= '0' && n <= '9')

void play_rtttl(char *p)
{
  // Absolutely no error checking in here

  byte default_dur = 4;
  byte default_oct = 6;
  int bpm = 63;
  int num;
  long wholenote;
  long duration;
  byte note;
  byte scale;

  // format: d=N,o=N,b=NNN:
  // find the start (skip name, etc)

  while(*p != ':') p++;    // ignore name
  p++;                     // skip ':'

  // get default duration
  if(*p == 'd')
  {
    p++; 
    p++;              // skip "d="
    num = 0;
    while(isdigit(*p))
    {
      num = (num * 10) + (*p++ - '0');
    }
    if(num > 0) default_dur = num;
    p++;                   // skip comma
  }

  Serial.print("ddur: "); 
  Serial.println(default_dur, 10);

  // get default octave
  if(*p == 'o')
  {
    p++; 
    p++;              // skip "o="
    num = *p++ - '0';
    if(num >= 3 && num <=7) default_oct = num;
    p++;                   // skip comma
  }

  Serial.print("doct: "); 
  Serial.println(default_oct, 10);

  // get BPM
  if(*p == 'b')
  {
    p++; 
    p++;              // skip "b="
    num = 0;
    while(isdigit(*p))
    {
      num = (num * 10) + (*p++ - '0');
    }
    bpm = num;
    p++;                   // skip colon
  }

  Serial.print("bpm: "); 
  Serial.println(bpm, 10);

  // BPM usually expresses the number of quarter notes per minute
  wholenote = (60 * 1000L / bpm) * 4;  // this is the time for whole note (in milliseconds)

  Serial.print("wn: "); 
  Serial.println(wholenote, 10);


  // now begin note loop
  while(*p)
  {
    // first, get note duration, if available
    num = 0;
    while(isdigit(*p))
    {
      num = (num * 10) + (*p++ - '0');
    }

    if(num) duration = wholenote / num;
    else duration = wholenote / default_dur;  // we will need to check if we are a dotted note after

      // now get the note
    note = 0;

    switch(*p)
    {
    case 'c':
      note = 1;
      break;
    case 'd':
      note = 3;
      break;
    case 'e':
      note = 5;
      break;
    case 'f':
      note = 6;
      break;
    case 'g':
      note = 8;
      break;
    case 'a':
      note = 10;
      break;
    case 'b':
      note = 12;
      break;
    case 'p':
    default:
      note = 0;
    }
    p++;

    // now, get optional '#' sharp
    if(*p == '#')
    {
      note++;
      p++;
    }

    // now, get optional '.' dotted note
    if(*p == '.')
    {
      duration += duration/2;
      p++;
    }

    // now, get scale
    if(isdigit(*p))
    {
      scale = *p - '0';
      p++;
    }
    else
    {
      scale = default_oct;
    }

    scale += OCTAVE_OFFSET;

    if(*p == ',')
      p++;       // skip comma for next note (or we may be at the end)

    // now play the note

    if(note)
    {
      Serial.print("Playing: ");
      Serial.print(scale, 10); 
      Serial.print(' ');
      Serial.print(note, 10); 
      Serial.print(" (");
      Serial.print(notes[(scale - 4) * 12 + note], 10);
      Serial.print(") ");
      Serial.println(duration, 10);
      tone1.play(notes[(scale - 4) * 12 + note]);
      delay(duration);
      tone1.stop();
    }
    else
    {
      Serial.print("Pausing: ");
      Serial.println(duration, 10);
      delay(duration);
    }
  }
}

void loadtimers(){
  //  alarm[0].DOW = 2;
  //  alarm[0].Hour = 18;
  //  alarm[0].Minute = 15;
  for(int i =0; i< 5; i++){
    alarm[i].DOW = i + 2;
    alarm[i].Hour = 07;
    alarm[i].Minute = 00;
  }

}

void show_arduino()
{
  display_img(img1);
  delay(2000);
  lcd_clear();
}

int convint(String Data){
  char carray2[Data.length() +1];
  Data.toCharArray(carray2,sizeof(carray2));
  return atoi(carray2); 
}



int smooth(int current, int previous, int factor) {
  return ((previous * (16 - factor) + (current * factor))) / 16;
}

void checkvoice(){
  for(int i =0;i<15;i++){
    if(millis() > (voice[i].trig + 5000)) { // 5 mins
      voice[i].active= false;
    }
  }


}
boolean disvoice = false;
void serialEvent1(){

  while(Serial1.available())
  {
    com = Serial1.read();
    if(disvoice){
      Serial2.print((char)com);
    }
    switch(com)
    {
    case 0x22:
      Serial2.println(map(constrain(analogRead(A15),20,780),20,780,1,255));
      if(map(constrain(analogRead(A15),20,780),20,780,1,255) < 100){
        sendRF(0,1,1,0);         
        Serial2.println("Lights On");
      }
      else{
        sendRF(0,1,0,0);
        Serial2.println("Lights Off");        
      }

      //      voice[0].active = true;
      //      voice[0].trig = millis();

      break;

    case 0x23:  
      Serial2.println("Pos3");
      Serial2.println("Snooze");
      ActiveSnooze = true;
      break;

    case 0x24:
      Serial2.println("Pos4");
      AlarmActive = !AlarmActive;
      Serial2.print("Alarm State: ");
      Serial2.println(AlarmActive);
      if (AlarmActive){
        EEPROM.write(50, 1);        
      }
      else{
        EEPROM.write(50, 0);        
      }
      break;

    case 0x25:
      Serial2.println("Pos5");   
      //      Serial2.println(com);
      break;

    case 0x26:
      Serial2.println("Pos6");

      break;

    default:
      Serial2.println(com);
      break;
    }
  }
  //if(disvoice){
  //    Serial2.println();
  //    }

}

String remotecmd;
boolean remotestart=false;
void serialEvent2(){
  while(Serial2.available()){
    char inchar =0;
    inchar =Serial2.read();
    Serial.write(inchar);
    remotecmd += inchar; 

  }
  ProcessCMD(remotecmd);
  remotecmd = 0x00;
}
void ProcessCMD(String Data){
  int i, count; 
  for (i=0, count=0; Data[i]; i++) 
    count += (Data[i] == ','); 

  String newData[count];

  for(int i =0; i < count;i++){
    int firstpos = Data.indexOf(",");
    newData[i] = Data.substring(0,firstpos);
    Data = Data.substring(firstpos+1,Data.length());
  }

  if(newData[0] == "power"){
    alertmsg(newData[1]);
    Serial2.println(newData[1]);

  }
  if(newData[0] == "msg"){
    alertmsg(newData[1]);
  }  

  if(newData[0] =="DoorBell"){
    remotemsg.ReceivedData = "DoorBell";
    remotemsg.DispTime = 30;
    remotemsg.Active = true;
    remotemsg.Actioned = false; 
    remotemsg.Sound = true;
  }
  if(newData[0] =="rg1"){

    Serial1.write(0xAA00);
    delay(500);
    Serial1.write(0xAA);
    Serial1.write(0x11);
    alertmsg("Record");
    disvoice = true;
  }
  if(newData[0] =="compact"){
    Serial1.write(0xAA);
    Serial1.write(0x37);
  } 
  if(newData[0] =="common"){
    Serial1.write(0xAA);
    Serial1.write(0x36);
  } 
  if(newData[0] =="beep"){
    tone1.stop();
    tone1.play(NOTE_A4);
    delay(100);
    tone1.stop();
  } 

  if(newData[0] == "lights"){
    boolean lstate=false;
    boolean gstate=false;


    if (convint(newData[3]) == 1){
      lstate=true;
    } 
    else {
      lstate=false;
    }

    if (convint(newData[4]) == 1){
      gstate=true;
    } 
    else {
      gstate=false;
    }
    Serial2.print("Light Set to: ");
    Serial2.println(lstate);
    sendRF(convint(newData[1]),convint(newData[2]),lstate,gstate); 

  }

}

void sendRF(unsigned long lSender,unsigned int iRecipient, boolean bOn,boolean bGroup){
  homeEasy.sendAdvancedProtocolMessage(lSender,iRecipient,bOn,bGroup);
  homeEasy.sendAdvancedProtocolMessage(lSender,iRecipient,bOn,bGroup);
  homeEasy.sendAdvancedProtocolMessage(lSender,iRecipient,bOn,bGroup);
}

//  sendRF(0,8,1,0); 
//      if(map(constrain(analogRead(A15),20,780),20,780,1,255) > 200){
//        sendRF(0,8,0,0);         
//      }
//      else{
//        sendRF(0,8,1,0); 
//      }






